using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using Microsoft.Xna.Framework;
using ProtoBuf.Meta;
using log4net;

namespace Game500.StServer.Src
{
  internal class TcpServerNetworkManager : NetworkManager
  {
    private const int DefaultListenPort = 5050;
    private static readonly ILog Log = LogManager.GetLogger(typeof (TcpServerNetworkManager));

    private readonly List<NetworkPlayer> myPlayers = new List<NetworkPlayer>();
    private TcpListener myTcpListener;

    #region Implementation of NetworkManager

    public override void Start()
    {
      if (myTcpListener == null)
      {
        myTcpListener = new TcpListener(IPAddress.Any, DefaultListenPort);
        myTcpListener.Start();
      }
      Log.Info(String.Format("Network started at port {0}. Waiting for clients", ((IPEndPoint)myTcpListener.LocalEndpoint).Port));
      myTcpListener.BeginAcceptTcpClient(TcpClientAcceptHandler, this);
    }

    public override void Dispose()
    {
      // TODO: dispose it properly!
      //myTcpListener.Stop();
    }

    public override int BindToPortRange(int minPort, int maxPort)
    {
      for (int i = minPort; i < maxPort; ++i)
      {
        try
        {
          myTcpListener = new TcpListener(IPAddress.Any, i);
          myTcpListener.Start();
          Port = i;
          return i;
        }
        catch
        {
          // If port allocation failed - just try next port
          myTcpListener.Stop();
        }
      }
      return 0;
    }

    private void TcpClientAcceptHandler(IAsyncResult ar)
    {
      TcpClient client = myTcpListener.EndAcceptTcpClient(ar);
      var player = new NetworkPlayer(client);
      player.PlayerDisconnected += PlayerDisconnectedHandler;
      player.PlayerJoined += PlayerJoinedHandler;
      myPlayers.Add(player);
      Log.Info(String.Format("New client connected from host {0}", client.Client.RemoteEndPoint));
      myTcpListener.BeginAcceptTcpClient(TcpClientAcceptHandler, this);
    }

    private void PlayerJoinedHandler(NetworkPlayer player)
    {
      RaisePlayerJoinedEvent(player);
    }

    private void PlayerDisconnectedHandler(NetworkPlayer player)
    {
      myPlayers.Remove(player);
      RaisePlayerLeftEvent(player);
      Log.Info(String.Format("Player disconnected: id {0}", player.CoreId.ID));
    }

    #endregion

    static TcpServerNetworkManager()
    {
      RuntimeTypeModel.Default.Add(typeof (Vector3), true).Add("X", "Y", "Z");
    }
  }
}